import threading
import time
from ipaddress import IPv4Network

from django.conf import settings
from django.urls import re_path, reverse
from django.utils.safestring import mark_safe

from stark.service.v1 import StarkHandler, Option
from web import models
from web.utils import ipv4_tools


def calculate_time(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        rtn = func(*args, **kwargs)
        end_time = time.time()
        print('任务开始时间：%s,任务结束时间:%s,任务总耗时：%s' % (start_time, end_time, end_time - start_time))
        return rtn

    return inner


def update_net_address(item):
    network_address = ipv4_tools.get_network_address(item.subnet)
    try:
        models.Hosts.objects.filter(ip_address=network_address).update(ip_type=settings.NET_ADDR_IP_TYPE)
    except Exception as ex:
        print(ex)


def update_broadcast_address(item):
    broadcast_address = ipv4_tools.get_broadcast_address(item.subnet)
    try:
        models.Hosts.objects.filter(ip_address=broadcast_address).update(
            ip_type=settings.BROADCAST_ADDR_IP_TYPE)
    except Exception as ex:
        print(ex)


def update_net_broadcast_address(item):
    """
    更新主机列表的网络地址和广播地址
    :param item: 子网对象
    :return:
    """
    network_address = ipv4_tools.get_network_address(item.subnet)
    network_obj = models.Hosts.objects.filter(ip_address=network_address).first()
    broadcast_address = ipv4_tools.get_broadcast_address(item.subnet)
    broadcast_obj = models.Hosts.objects.filter(ip_address=broadcast_address).first()
    try:
        if network_obj:
            network_id = network_obj.id
            models.Hosts.objects.filter(id=network_id).update(ip_type=settings.NET_ADDR_IP_TYPE)
        if broadcast_obj:
            broadcast_id = broadcast_obj.id
            models.Hosts.objects.filter(id=broadcast_id).update(ip_type=settings.BROADCAST_ADDR_IP_TYPE)
    except Exception as ex:
        print(ex)


def update_subnet_id(item):
    """
    更新主机列表的子网号
    :param item: 子网对象
    :return:
    """
    network_address_num = ipv4_tools.get_network_address(item.subnet, data_type='int')
    broadcast_address_num = ipv4_tools.get_broadcast_address(item.subnet, data_type='int')
    subnet_id = item.id
    host_id_list = [item.id for item in
                    models.Hosts.objects.filter(ip_address_num__lte=broadcast_address_num,
                                                ip_address_num__gte=network_address_num)]
    try:
        models.Hosts.objects.filter(ip_address_num__lte=broadcast_address_num,
                                    ip_address_num__gte=network_address_num).update(ip_subnet_id=subnet_id)
        # models.Hosts.objects.filter(id__in=host_id_list).update(ip_subnet_id=subnet_id)
    except Exception as ex:
        print(ex)


@calculate_time
def update_address_subnet(item):
    # update_net_broadcast_address(item)
    # update_subnet_id(item)
    network_address = ipv4_tools.get_network_address(item.subnet)
    broadcast_address = ipv4_tools.get_broadcast_address(item.subnet)
    try:
        models.Hosts.objects.filter(ip_address=network_address).update(ip_type=settings.NET_ADDR_IP_TYPE)
        models.Hosts.objects.filter(ip_address=broadcast_address).update(
            ip_type=settings.BROADCAST_ADDR_IP_TYPE)
    except Exception as ex:
        print(ex)
        print(threading.current_thread().name)

    # lock.release()
    network_address_num = ipv4_tools.get_network_address(item.subnet, data_type='int')
    broadcast_address_num = ipv4_tools.get_broadcast_address(item.subnet, data_type='int')
    subnet_id = item.id
    try:
        models.Hosts.objects.filter(ip_address_num__lte=broadcast_address_num,
                                    ip_address_num__gte=network_address_num).update(ip_subnet_id=subnet_id)
    except Exception as ex:
        print(ex)
        print(threading.current_thread().name)


def get_subnet_count(text, field):
    """
    获取子网号及子网数量
    :param text: choice字段的名称
    :param field: choice字段
    :return:
    """

    def inner(self, obj=None, is_header=None):
        if is_header:
            return text
        id = getattr(obj, field)
        subnet_obj = models.IpSubnet.objects.filter(id=id).first()
        subnet = subnet_obj.subnet
        subnet_count = models.IpSubnet.objects.filter(pid_id=id).count()
        if subnet_count > 0:
            str_subnet = "<span style='color:green;'>%s-子网数量：%s个</span>" % (subnet, subnet_count)
        else:
            str_subnet = "<span style='color:black;'>%s-子网数量：%s个</span>" % (subnet, subnet_count)
        return mark_safe(str_subnet)

    return inner


class IpSubnetHandler(StarkHandler):

    def display_subnet(self, obj=None, is_header=None, *args, **kwargs):
        if is_header:
            return '子网'
        record_url = reverse('stark:web_ipsubnet_plan_list', kwargs={'subnet_id': obj.id})
        return mark_safe('<a href="%s">子网规划</a>' % record_url)

    list_display = [StarkHandler.display_checkbox, get_subnet_count('子网号(数量)', 'id'), display_subnet]

    def get_urls(self):
        """
        二次路由分发
        :return:
        """
        patterns = [
            re_path(r'^list/$', self.wrapper(self.list_view), name=self.get_list_url_name),
            # re_path(r'^add/$', self.wrapper(self.add_view), name=self.get_add_url_name),
            # re_path(r'^change/(?P<pk>\d+)/$', self.wrapper(self.change_view),
            #         name=self.get_change_url_name),
            # re_path(r'^delete/(?P<pk>\d+)/$', self.wrapper(self.delete_view),
            #         name=self.get_delete_url_name)
        ]
        patterns.extend(self.extra_url())
        return patterns

    def get_list_display(self, request, *args, **kwargs):
        """
        预留列表页面显示字段的钩子函数
        :return:
        """
        value = []
        if self.list_display:
            value.extend(self.list_display)
        return value

    def get_add_btn(self, request, *args, **kwargs):
        return None

    @calculate_time
    def action_multi_init_subnet(self, request, net_count, *args, **kwargs):
        pk_list = request.POST.getlist('pk')
        total_time = 0
        for pk in pk_list:
            ipv4_subnet_object = models.IpSubnet.objects.filter(id=pk).first()
            if not ipv4_subnet_object:
                continue
            child_subnet_exists = models.IpSubnet.objects.filter(pid__isnull=False, pid=pk)
            if child_subnet_exists:
                continue
            subnet = ipv4_subnet_object.subnet
            ipv4_network_object = IPv4Network(subnet)
            prefix_length = ipv4_network_object.prefixlen
            if prefix_length > 24:
                continue
            ipv4_network_list = [item for item in list(ipv4_network_object.subnets(new_prefix=net_count))]
            ipv4_object_list = []
            ip_network_id = ipv4_subnet_object.ip_network_id
            for item in ipv4_network_list:
                ipv4_object_list.append(models.IpSubnet(ip_network_id=ip_network_id, pid_id=pk, subnet=str(item),
                                                        subnet_num=int(item.network_address)))
            models.IpSubnet.objects.bulk_create(ipv4_object_list, batch_size=30)
            ip_subnet_queryset = models.IpSubnet.objects.filter(subnet__in=ipv4_network_list)
            thread_net_list = []
            thread_subnet_list = []
            for item in ip_subnet_queryset:
                """
                方法一：串行执行任务
                第一步：更新广播地址；
                第二步：更新网络地址；
                第三步：更新IP地址所属的子网号；
                """
                update_net_address(item)
                update_broadcast_address(item)
                # update_net_broadcast_address(item)
                update_subnet_id(item)
                """
                方法二：并行执行任务（由于数据库行锁的存在，下面两步不能同时执行
                第一步：同时更新广播地址和网络地址；
                第二步：更新IP地址所属的子网号
                """
            #     t_net_obj = threading.Thread(target=update_net_broadcast_address, args=(item,))
            #     thread_net_list.append(t_net_obj)
            #     t_subnet_obj = threading.Thread(target=update_subnet_id, args=(item,))
            #     thread_subnet_list.append(t_subnet_obj)
            # for thread in thread_net_list:
            #     thread.start()
            # for thread in thread_net_list:
            #     thread.join()
            # for thread in thread_subnet_list:
            #     thread.start()
            # for thread in thread_subnet_list:
            #     thread.join()

    def action_multi_init_subnet_25(self, request, *args, **kwargs):
        """
        初始化两个子网
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        self.action_multi_init_subnet(request, 25, *args, **kwargs)

    action_multi_init_subnet_25.text = '初始化25位掩码子网'

    def action_multi_init_subnet_26(self, request, *args, **kwargs):
        """
        初始化两个子网
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        self.action_multi_init_subnet(request, 26, *args, **kwargs)

    action_multi_init_subnet_26.text = '初始化26位掩码子网'

    def action_multi_init_subnet_27(self, request, *args, **kwargs):
        """
        初始化两个子网
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        self.action_multi_init_subnet(request, 27, *args, **kwargs)

    action_multi_init_subnet_27.text = '初始化27位掩码子网'

    def action_multi_init_subnet_28(self, request, *args, **kwargs):
        """
        初始化两个子网
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        self.action_multi_init_subnet(request, 28, *args, **kwargs)

    action_multi_init_subnet_28.text = '初始化28位掩码子网'

    def action_multi_init_subnet_29(self, request, *args, **kwargs):
        """
        初始化两个子网
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        self.action_multi_init_subnet(request, 29, *args, **kwargs)

    action_multi_init_subnet_29.text = '初始化29位掩码子网'

    def action_multi_init_subnet_30(self, request, *args, **kwargs):
        """
        初始化两个子网
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        self.action_multi_init_subnet(request, 30, *args, **kwargs)

    action_multi_init_subnet_30.text = '初始化30位掩码子网'

    action_list = [action_multi_init_subnet_25, action_multi_init_subnet_26, action_multi_init_subnet_27,
                   action_multi_init_subnet_28, action_multi_init_subnet_29, action_multi_init_subnet_30]

    search_group = [Option('ip_network')]
    search_list = ['subnet__contains']

    def get_queryset(self, request, *args, **kwargs):
        return self.model_class.objects.filter(pid_id__isnull=True)
